import { getRandom, addEvent } from './utils'

interface StarOptions {
  el: string;
  [key: string]: any; // 允许额外的属性
}

interface CanvasEle {
  canvas: HTMLCanvasElement;
  ctx: CanvasRenderingContext2D; // 允许额外的属性
}

class createBack {
  canvas: HTMLCanvasElement
  ctx: CanvasRenderingContext2D
  canvasEle: CanvasEle
  groupInstance: Group | undefined
  starInstance: Star | undefined
  constructor() {
    this.canvas = document.createElement('canvas')
    this.canvas.id = 'starCanvas'
    this.ctx = this.canvas.getContext('2d') as CanvasRenderingContext2D
    this.canvasEle = { canvas: this.canvas, ctx: this.ctx }
    this.groupInstance = undefined
    this.starInstance = undefined
  }
  create(options: StarOptions) {
    this.starInstance = new Star(this.canvasEle, options)
    return this
  }

  drawStar(pointNum: number | undefined, maxDis: number | undefined, speed: number | undefined) {
    this.groupInstance = new Group(this.canvasEle, pointNum, maxDis, speed)
    this.groupInstance.draw()
    return this
  }

  clickPoint(num: number = 50, addNum: number = 5) {
    if (this.groupInstance) {
      addEvent(this.canvasEle.canvas, 'click', () => {
        if (this.groupInstance!.points.length >= num) return false;
        for (let i = 0; i < addNum; i++) {
          this.groupInstance!.points.push(new Point(this.canvasEle, undefined, undefined))
        }
      })
    }
  }

  clear() {
    this.ctx!.clearRect(0, 0, this.canvas.width, this.canvas.height);
    this.starInstance?.removeListener()
  }
}

class Star {
  options: StarOptions
  width: number | string
  height: number | string
  canvasEle: CanvasEle
  private resizeListener: ((this: Window, ev: Event) => any) | null;
  constructor(canvasEle: CanvasEle, options: StarOptions = { el: 'body' }) {
    this.options = options;
    this.width = 0;
    this.height = 0;
    this.canvasEle = canvasEle
    this.resizeListener = null
    this.init()
  }
  init(): void {
    this.drawBack();
  }
  drawBack(): void {
    this.screenWidth(this.options.el)
    this.resizeListener = () => {
      this.screenWidth(this.options.el)
    }
    window.addEventListener('resize', this.resizeListener)
  }
  screenWidth(el: any): void {
    // 获取屏幕宽度
    const ele = document.querySelector(el);
    let canvasDom = document.getElementById('starCanvas')
    if (!canvasDom) ele!.appendChild(this.canvasEle.canvas);
    this.width = ele!.clientWidth as number;
    this.height = ele!.clientHeight as number;
    this.canvasEle.canvas.width = this.width * devicePixelRatio;
    this.canvasEle.canvas.height = this.height * devicePixelRatio;
  }
  removeListener(): void {
    if (this.resizeListener) {
      window.removeEventListener('resize', this.resizeListener);
      this.resizeListener = null; // 清除引用
    }
  }
}

class Point {
  x: number
  y: number
  r: number
  speedX: number
  speedY: number
  lastDrawTime: number | null
  canvasEle: CanvasEle
  constructor(canvasEle: CanvasEle, x: number | undefined, y: number | undefined, speed: number = 50, r: number = 2) {
    this.canvasEle = canvasEle
    this.r = r;
    this.x = x || getRandom(this.r / 2, this.canvasEle.canvas.width - this.r / 2);
    this.y = y || getRandom(this.r / 2, this.canvasEle.canvas.height - this.r / 2);
    this.speedX = getRandom(speed * -1, speed);
    this.speedY = getRandom(speed * -1, speed);
    this.lastDrawTime = null;
  }
  draw() {
    if (this.lastDrawTime) {
      let nowDrawTime = Date.now();
      let time = (nowDrawTime - this.lastDrawTime) / 1000;
      this.x = this.x + this.speedX * time;
      this.y = this.y + this.speedY * time;
      if (this.x <= this.r / 2 || this.x >= this.canvasEle.canvas.width - this.r / 2) {
        this.speedX *= -1;
      }
      if (this.y <= this.r / 2 || this.y >= this.canvasEle.canvas.height - this.r / 2) {
        this.speedY *= -1;
      }
    }
    this.canvasEle.ctx!.beginPath();
    this.canvasEle.ctx!.arc(this.x, this.y, this.r, 0, 2 * Math.PI);
    this.canvasEle.ctx!.fillStyle = 'rgb(200,200,200)';
    this.canvasEle.ctx!.fill()
    this.lastDrawTime = Date.now();
  }

  mousePoint(x: number, y: number) {
    this.canvasEle.ctx!.beginPath();
    this.canvasEle.ctx!.arc(x, y, this.r, 0, 2 * Math.PI);
    this.canvasEle.ctx!.fillStyle = 'rgb(200,200,200)';
    this.canvasEle.ctx!.fill();
  }
}

class Group {
  points: Point[];
  maxDistance: number;
  mouseStar: Point;
  canvasEle: CanvasEle
  constructor(canvasEle: CanvasEle, pointNumber = 40, maxDistance = 500, speed: number | undefined) {
    this.canvasEle = canvasEle
    this.points = new Array(pointNumber).fill(0).map(() => new Point(this.canvasEle, undefined, undefined, speed));
    this.maxDistance = maxDistance;
    this.mouseStar = new Point(this.canvasEle, undefined, undefined);
    addEvent(this.canvasEle.canvas, 'mousemove', (e: any) => {
      this.mouseStar.x = e.clientX;
      this.mouseStar.y = e.clientY;
    })
  }
  draw() {
    requestAnimationFrame(() => {
      this.draw();
    })
    this.canvasEle.ctx!.clearRect(0, 0, this.canvasEle.canvas.width, this.canvasEle.canvas.height);
    this.mouseStar.mousePoint(this.mouseStar.x, this.mouseStar.y);
    for (let i = 0; i < this.points.length; i++) {
      const p1 = this.points[i];
      p1.draw();
      for (let j = i + 1; j < this.points.length; j++) {
        const p2 = this.points[j];
        p2.draw();
        //这个表达式在编程中通常用来计算两点之间的欧几里得距离（直线距离）
        const distance = Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
        if (distance > this.maxDistance) {
          continue;
        }
        this.canvasEle.ctx!.beginPath();
        this.canvasEle.ctx!.moveTo(p1.x, p1.y);
        this.canvasEle.ctx!.lineTo(p2.x, p2.y);
        this.canvasEle.ctx!.strokeStyle = `rgba(200,200,200,${1 - (distance / this.maxDistance)})`;
        this.canvasEle.ctx!.stroke();
      }
      if ((Math.abs(this.mouseStar.x - p1.x) <= this.maxDistance / 2
        && Math.abs(this.mouseStar.y - p1.y) <= this.maxDistance / 2)) {
        this.canvasEle.ctx!.beginPath();
        this.canvasEle.ctx!.moveTo(p1.x, p1.y);
        this.canvasEle.ctx!.lineTo(this.mouseStar.x, this.mouseStar.y);
        this.canvasEle.ctx!.strokeStyle = `rgba(200,200,200,1)`;
        this.canvasEle.ctx!.stroke();
      }
    }
  }
}



export default createBack


